#ifndef SRC_NATIVE_API_COMMON_H_
#define SRC_NATIVE_API_COMMON_H_

#define NAPI_RETVAL_NOTHING

#define GET_AND_THROW_LAST_ERROR(env)                                                                     \
    do {                                                                                                  \
        const napi_extended_error_info *error_info = nullptr;                                             \
        napi_get_last_error_info((env), &error_info);                                                     \
        bool is_pending = false;                                                                          \
        napi_is_exception_pending((env), &is_pending);                                                    \
        if (!is_pending && error_info != nullptr) {                                                       \
            const char* error_message =                                                                   \
                error_info->error_message != nullptr ? error_info->error_message : "empty error message"; \
            napi_throw_error((env), nullptr, error_message);                                              \
        }                                                                                                 \
    } while (0)

#define NAPI_ASSERT_BASE(env, assertion, message, ret_val)                                   \
    do {                                                                                     \
        if (!(assertion)) {                                                                  \
            napi_throw_error((env), nullptr, "assertion (" #assertion ") failed: " message); \
            return ret_val;                                                                  \
        }                                                                                    \
    } while (0)

#define NAPI_ASSERT(env, assertion, message)           \
    NAPI_ASSERT_BASE(env, assertion, message, nullptr)

#define NAPI_ASSERT_RETURN_VOID(env, assertion, message)           \
    NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING)

#define NAPI_CALL_BASE(env, the_call, ret_val) \
    do {                                       \
        if ((the_call) != napi_ok) {           \
            GET_AND_THROW_LAST_ERROR((env));   \
            return ret_val;                    \
        }                                      \
    } while (0)

#define NAPI_CALL(env, the_call)           \
    NAPI_CALL_BASE(env, the_call, nullptr)

#define NAPI_CALL_RETURN_VOID(env, the_call)           \
    NAPI_CALL_BASE(env, the_call, NAPI_RETVAL_NOTHING)

#define DECLARE_NAPI_PROPERTY(name, val)                                         \
    {                                                                            \
        (name), nullptr, nullptr, nullptr, nullptr, (val), napi_default, nullptr \
    }

#define DECLARE_NAPI_STATIC_PROPERTY(name, val)                                 \
    {                                                                           \
        (name), nullptr, nullptr, nullptr, nullptr, (val), napi_static, nullptr \
    }

#define DECLARE_NAPI_FUNCTION(name, func)                                         \
    {                                                                             \
        (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \
    }

#define DECLARE_NAPI_STATIC_FUNCTION(name, func)                                 \
    {                                                                            \
        (name), nullptr, (func), nullptr, nullptr, nullptr, napi_static, nullptr \
    }

#define DECLARE_NAPI_GETTER(name, getter)                                           \
    {                                                                               \
        (name), nullptr, nullptr, (getter), nullptr, nullptr, napi_default, nullptr \
    }

#define DECLARE_NAPI_SETTER(name, setter)                                           \
    {                                                                               \
        (name), nullptr, nullptr, nullptr, (setter), nullptr, napi_default, nullptr \
    }

#define DECLARE_NAPI_GETTER_SETTER(name, getter, setter)                             \
    {                                                                                \
        (name), nullptr, nullptr, (getter), (setter), nullptr, napi_default, nullptr \
    }

#define RETURN_STATUS_IF_FALSE(env, condition, status) \
    if (!(condition)) {                                \
        return napi_set_last_error((env), (status));   \
    }

#define CHECK_ENV(env)           \
    if ((env) == nullptr) {      \
        return napi_invalid_arg; \
    }

#define CHECK_ARG(env, arg)                                             \
    RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg)

#endif // SRC_NATIVE_API_COMMON_H_
